﻿using System;
using System.Diagnostics.Contracts;
using System.Windows.Input;

namespace Socium.Presentation {
	public abstract class FormViewModel<T> : PageViewModel, IFormViewModel where T : class {
		protected FormViewModel(T model) : base(string.Empty) {
			_model = model;

			SaveCommand = new Command(Save, CanSave);
			CancelCommand=new Command(Cancel);

			if (model != null) {
				DisplayName = GetDisplayName(model);
				Update();
			}
			else {
				DisplayName = GetDefaultDisplayName();
			}
		}

		protected FormViewModel() : this(null) {}

		public ICommand SaveCommand { get; private set; }
		public ICommand CancelCommand { get; private set; }

		T _model;

		public bool IsNew {
			get { return _model == null; }
		}

		protected abstract T CreateModel();
		protected abstract void Update(T model);

		public abstract bool CanSave();
		protected abstract void Save(T model);
		
		[Pure]
		protected abstract string GetDisplayName(T model);

		[Pure]
		protected abstract string GetDefaultDisplayName();

		void Save() {
			if (!CanSave()) {
				throw new InvalidOperationException();
			}

			var new_model = _model == null;
			if (new_model) {
				_model = CreateModel();
				
			}

			Save(_model);

			if (new_model) {
				OnModelCreated();
			} 
			else {
				OnModelUpdated();
			}

			OnSaved();
		}

		void OnModelCreated() {
			var handler = ModelCreated;
			if (handler != null) {
				handler(_model);
			}
		}

		void OnModelUpdated() {
			var handler = ModelUpdated;
			if (handler != null) {
				handler();
			}
		}

		void OnSaved() {
			var handler = Saved;
			if (handler != null) {
				handler();
			}
		}

		public void Update() {
			Update(_model);
		}

		void Cancel() {
			var handler = Canceled;
			if (handler != null) {
				handler();
			}
		}

		public event Action Canceled;
		public event Action<T> ModelCreated;
		public event Action ModelUpdated;
		public event Action Saved;
	}
}
